package com.mouse.jhttp.Proxy;

import com.mouse.jhttp.annotation.JClient;
import com.mouse.jhttp.annotation.header.*;
import com.mouse.jhttp.annotation.request.Delete;
import com.mouse.jhttp.annotation.request.Get;
import com.mouse.jhttp.annotation.request.Post;
import com.mouse.jhttp.annotation.request.Put;
import com.mouse.jhttp.entity.HttpHeader;
import com.mouse.jhttp.entity.HttpHeaders;
import com.mouse.jhttp.enums.RequestMethod;
import com.mouse.jhttp.util.JsonUtil;
import com.mouse.jhttp.util.StringUtil;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class JHttpInterceptor implements MethodInterceptor {
    private final static String code = "UTF-8";
    //请求地址-前缀
    private String baseUrl = "";

    //请求地址-后缀
    private String suffixUrl = "";
    //所有请求头
    private List<BasicHeader> headerList = new ArrayList<>();
    // 请求类型
    private RequestMethod requestMethod = null;

    private void clear() {
        baseUrl = "";
        suffixUrl = "";
        headerList = new ArrayList<>();
        requestMethod = null;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        clear();
        Class<?> clas = method.getDeclaringClass();
        initConfig(clas.getAnnotations());//类注解
        initConfig(method.getDeclaredAnnotations());//方法注解
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpRequestBase requestBase = null;
        String url = baseUrl + suffixUrl;
        if (RequestMethod.GET.equals(requestMethod)) {
            requestBase = new HttpGet(turnUrl(url));
        } else if (RequestMethod.POST.equals(requestMethod)) {
            requestBase = new HttpPost(turnUrl(url));
        } else if (RequestMethod.PUT.equals(requestMethod)) {
            requestBase = new HttpPut(turnUrl(url));
        } else if (RequestMethod.DELETE.equals(requestMethod)) {
            requestBase = new HttpDelete(turnUrl(url));
        }
        requestBase.setHeaders(headerList.stream().toArray(value -> new BasicHeader[value]));
        CloseableHttpResponse response = httpClient.execute(requestBase);
        String content = EntityUtils.toString(response.getEntity());
        Object result = null;
        Class<?> returnType = method.getReturnType();
        if (returnType.equals(String.class)) {
            result = content;
        } else if (returnType.equals(com.mouse.jhttp.entity.Response.class))
            result = toResponse(response, method, content);
        else {
            result = JsonUtil.getObject(content, returnType);
        }
        response.close();
        httpClient.close();
        return result;
    }

    private String turnUrl(String url) {
        url = URLDecoder.decode(url);
        StringBuilder sb = new StringBuilder();
        String[] split = url.split("\\?", 2);
        if (split.length == 2) {
            sb.append(split[0]);
            sb.append("?");
            String param = split[1];
            String[] paramSplit = param.split("=");
            for (int i = 0; i < paramSplit.length; i++) {
                if (i % 2 == 0) {
                    sb.append(paramSplit[i]);
                    sb.append("=");
                } else if (i % 2 == 1) {
                    sb.append(URLEncoder.encode(paramSplit[i]));
                    if (i != paramSplit.length) sb.append("&");
                }
            }
        } else {
            sb.append(url);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String str = "111?11?111";
        System.out.println(str.split("\\?", 2).length);
    }

    private Object toResponse(CloseableHttpResponse response, Method method, String content) throws Exception {
        Class<?> returnType = method.getReturnType();
        if (returnType == null) return null;
        com.mouse.jhttp.entity.Response response1 = (com.mouse.jhttp.entity.Response) returnType.newInstance();
        Type type = method.getGenericReturnType();
        if (type instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) type).getActualTypeArguments();
            if (types.length == 1) {
                Class cla = (Class) types[0];
                if (cla.equals(String.class)) {
                    response1.setData(content);
                } else {
                    response1.setData(JsonUtil.getObject(content, cla));
                }
            }
        }
        response1.setStatus(response.getStatusLine().getStatusCode());
        List<HttpHeader> headerList = Arrays.stream(response.getAllHeaders()).map(header -> new HttpHeader(header.getName(), header.getValue())).collect(Collectors.toList());
        response1.setHeaderList(headerList);
        return response1;
    }

    private void initConfig(Annotation[] annotations) {
        for (Annotation annotation : annotations) {
            if (annotation instanceof JClient) {
                JClient jClient = (JClient) annotation;
                baseUrl = jClient.value();
            } else if (annotation instanceof Get) {
                Get get = (Get) annotation;
                suffixUrl = get.value();
                requestMethod = RequestMethod.GET;
            } else if (annotation instanceof Post) {
                Post post = (Post) annotation;
                suffixUrl = post.value();
                requestMethod = RequestMethod.POST;
            } else if (annotation instanceof Put) {
                Put put = (Put) annotation;
                suffixUrl = put.value();
                requestMethod = RequestMethod.PUT;
            } else if (annotation instanceof Delete) {
                Delete delete = (Delete) annotation;
                suffixUrl = delete.value();
                requestMethod = RequestMethod.DELETE;
            } else if (annotation instanceof ContentType) {
                ContentType contentType = (ContentType) annotation;
                headerList.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, contentType.value()));
            } else if (annotation instanceof Cookie) {
                Cookie cookie = (Cookie) annotation;
                if (StringUtil.isNotBlank(cookie.key()))
                    headerList.add(new BasicHeader(HttpHeaders.COOKIE, cookie.key() + ":" + cookie.value()));
            } else if (annotation instanceof Cookies) {
                Cookies cookies = (Cookies) annotation;
                if (cookies.value() != null) {
                    for (Cookie cookie : cookies.value()) {
                        if (StringUtil.isBlank(cookie.key())) continue;
                        headerList.add(new BasicHeader(HttpHeaders.COOKIE, cookie.key() + ":" + cookie.value()));
                    }
                }
            } else if (annotation instanceof CookiesStr) {
                CookiesStr cookiesStr = (CookiesStr) annotation;
                headerList.add(new BasicHeader(HttpHeaders.COOKIE, cookiesStr.value()));
            } else if (annotation instanceof Referer) {
                Referer referer = (Referer) annotation;
                headerList.add(new BasicHeader(HttpHeaders.REFERER, referer.value()));
            } else if (annotation instanceof UserAgent) {
                UserAgent userAgent = (UserAgent) annotation;
                headerList.add(new BasicHeader(HttpHeaders.USER_AGENT, userAgent.value()));
            } else if (annotation instanceof Header) {
                Header header = (Header) annotation;
                headerList.add(new BasicHeader(header.key(), header.value()));
            } else if (annotation instanceof Headers) {
                Headers headers = (Headers) annotation;
                if (headers.value() != null) {
                    for (Header header : headers.value()) {
                        headerList.add(new BasicHeader(header.key(), header.value()));
                    }
                }
            }
        }
    }
}
